<?php
/* Plugin API
--------------------------------------------- */
/**
 * Almacena los filtros/acciones.
 *
 * @var array
 **/
$filters = array();

/**
 * Almacena los filtros/acciones actuales.
 *
 * @var array
 **/
$current_filter = array();

/**
 * Guarda el numero de veces que una accion ha sido ejecutada.
 *
 * @var array
 **/
$actions = array();

/* Filtros
--------------------------------------------- */
function add_filter( $tag, $callback, $priority = 10, $arguments = 1 ){
	$id = _filter_unique_id( $tag, $callback, $priority );
	$GLOBALS['filters'][$tag][$priority][$id] = array( 'callback' => $callback, 'arguments' => $arguments );
	return true;
}


// Elimina un filtro
function remove_filter( $tag, $callback, $priority = 10, $arguments = 1 ){
	$id = _filter_unique_id( $tag, $callback, $priority );

	$r = isset( $GLOBALS['filters'][$tag][$priority][$id] );
	if( true === $r ){
		unset( $GLOBALS['filters'][$tag][$priority][$id] );
		if( empty( $GLOBALS['filters'][$tag][$priority] ) )
			unset( $GLOBALS['filters'][$tag][$priority] );
	}
	return $r;
}

// Elimina todos los filtros
function remove_all_filters($tag, $priority = false) {
	global $filters;

	if( isset( $filters[$tag] ) ) {
		if( false !== $priority && isset($filters[$tag][$priority]) )
			unset( $filters[$tag][$priority] );
		else
			unset( $filters[$tag] );
	}

	return true;
}

// Indica si un filtro tiene funciones o no
function has_filter($tag, $callback_to_check = false) {
	global $filters;

	$has = !empty( $filters[$tag] );
	if ( false === $callback_to_check || false == $has )
		return $has;

	if ( !$id = _filter_unique_id($tag, $callback_to_check, false) )
		return false;

	foreach ( (array) array_keys($filters[$tag]) as $priority ) {
		if ( isset($filters[$tag][$priority][$id]) )
			return $priority;
	}

	return false;
}

// Devuelve el nombre del filtro/accion actual
function current_filter(){
	global $current_filter;
	return current( $current_filter );
}

// Aplica los filtros almacenados
function apply_filters( $tag, $value ){
	global $filters, $current_filter;

	// Ejecuta las funciones 'all'
	if( isset( $filters['all'] ) ){
		$current_filter[] = $tag;
		$all_args = func_get_args();
		_call_all_hook( $all_args );
	}

	if ( !isset( $filters[$tag] ) ) {
		if ( isset( $filters['all'] ) )
			array_pop( $current_filter );
		return;
	}

	// Si no esta agregado 'all'  alamacena el tag actual
	if ( !isset( $filters['all'] ) )
		$current_filter[] = $tag;

	reset( $filters[ $tag ] );

	$args = array();
	if ( empty($args) )
		$args = func_get_args();

	while( current($filters[ $tag ]) !== false ){
		foreach( (array) current($filters[ $tag ]) as $the )
			if( !is_null( $the['callback'] ) ){
				$args[1] = $value;
				$value = call_user_func_array( $the['callback'], array_slice($args, 1, (int) $the['arguments'] ) );
			}
		next($filters[ $tag ]);
	}
	
	array_pop($current_filter);

	return $value;
}

/* Acciones
--------------------------------------------- */
// Agrega una accion
function add_action( $tag, $callback, $priority = 10, $arguments = 1 ){
	return add_filter( $tag, $callback, $priority, $arguments );
}

// Retorna un boolean si una accion contiene funciones
function has_action( $tag, $callback_to_check = false ){
	return has_filter( $tag, $callback_to_check );
}

// Elimina una funcion dentro de una accion
function remove_action($tag, $callback, $priority = 10, $arguments = 1) {
	return remove_filter($tag, $callback, $priority, $arguments);
}

// Elimina todas funciones dentro de una accion
function remove_all_actions($tag, $priority = false) {
	return remove_all_filters($tag, $priority);
}

// Ejecuta las acciones
function do_action( $tag, $arg = '' ){
	global $filters, $current_filter, $actions;

	// Agrega el numero de funciones que la accion es ejecutada
	if ( ! isset( $actions ) )
		$actions = array();

	if ( ! isset( $actions[$tag] ) )
		$actions[ $tag ] = 1;
	else
		++$actions[ $tag ];

	// Ejecuta las funciones 'all'
	if( isset( $filters['all'] ) ){
		$current_filter[] = $tag;
		$all_args = func_get_args();
		_call_all_hook( $all_args );
	}

	if ( !isset( $filters[$tag] ) ) {
		if ( isset( $filters['all'] ) )
			array_pop( $current_filter );
		return;
	}

	// Si no esta agregado 'all'  alamacena el tag actual
	if ( !isset( $filters['all'] ) )
		$current_filter[] = $tag;

	$args = array();
	if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) ) // array(&$this)
		$args[] =& $arg[0];
	else
		$args[] = $arg;
	for ( $a = 2; $a < func_num_args(); $a++ )
		$args[] = func_get_arg($a);

	reset( $filters[ $tag ] );

	while( current($filters[ $tag ]) !== false ){
		foreach( (array) current($filters[ $tag ]) as $the )
			if( !is_null( $the['callback'] ) )
				call_user_func_array( $the['callback'], array_slice($args, 0, (int) $the['arguments'] ) );
		next($filters[ $tag ]);
	}
	
	array_pop($current_filter);
}

// Devuelve el numero de veces que una accion es lanzada
function did_action( $tag ){
	global $actions;

	if ( ! isset( $actions ) || ! isset( $actions[$tag] ) )
		return 0;

	return $actions[$tag];
}

// Llama a todas las funciones de 'all'
function _call_all_hook( $arg ){
	global $filters;
	
	reset( $filters['all'] );
	while( current($filters['all']) !== false ){
		foreach( (array) current($filters['all']) as $the )
			if( !is_null( $the['callback'] ) )
				call_user_func_array( $the['callback'], $arg );
		next($filters[$tag]);
	}
}

// Genera un ID unico para cada metodo
function _filter_unique_id( $tag, $callback, $priority ){
	global $filters;
	static $filter_id_count = 0;

	if( is_string($callback) )
		return $callback;

	if( is_object($callback) )
		$callback = array( $callback, '' );
	else
		$callback = (array)$callback;

	if( is_object($callback[0]) ){
		
		if( function_exists('spl_object_hash') ){
			return spl_object_hash($callback[0]) . $callback[1];
		} else {
			$id = get_class( $callback[0] ) . $callback[1];

			if( !isset($callback[0]->filter_id) ){
				if( false === $priority )
					return false;
				$id .= isset( $filters[$tag][$priority] ) ? count( (array)$filters[$tag][$priority] ) : $filter_id_count;
				$callback[0]->filter_id = $filter_id_count;
				++$filter_id_count;
			} else {
				$id .= $callback[0]->filter_id;
			}
		}
	} else if( is_string($callback[0]) ){
		return $callback[0]	. $callback[1];
	}	
}
?>